package mgo

import (
	"reflect"
	"time"

	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
	"hub.hddznet.com/uniplatform/framework/logging"
	"hub.hddznet.com/app/framework/errs"
	"hub.hddznet.com/app/framework/tools"
)

type MgoDatabase struct {
	*mgo.Database
}

//封装mgo的collection，对于增删改进行二次封装
type MgoCollection struct {
	*mgo.Collection
}

//可同时插入多个对象
func (this MgoCollection) HDInsert(objs ... interface{}) (error) {
	for _, o := range objs {
		ty := reflect.TypeOf(o)
		if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Struct {
			panic("注入对象必须为指针对象，并且指向的必须是struct")
		}
	}
	oos := make([]interface{}, 0)
	for _, o := range objs {
		this.hdCommon(o, bson.NewObjectId().Hex())
		oos = append(oos, o)
	}
	err := this.Insert(oos...)
	if err != nil {
		logging.Error("批量插入失败", err)
		return errs.MgoInsertErr(err)
	}
	return nil
}

/*
	排序规则，例如：
	version:正序
	-version:反序
 */
func (this MgoCollection) HDGetOne(query interface{}, obj interface{}, sort ... string) error {

	ty := reflect.TypeOf(obj)
	if ty.Kind() != reflect.Ptr || ty.Elem().Kind() != reflect.Struct {
		panic("注入对象必须为指针对象，并且指向的必须是struct")
	}

	ty = reflect.SliceOf(ty.Elem())
	vl := reflect.New(ty)
	results := vl.Interface()
	sess := this.Find(query)
	if len(sort) > 0 {
		sess.Sort(sort...)
	}
	err := sess.All(results)
	if err != nil {
		logging.Error("查询出错", err)
		return errs.MgoSearchErr(err)
	}

	if vl.Elem().Len() > 0 {
		reflect.ValueOf(obj).Elem().Set(vl.Elem().Index(0))
		return nil
	}
	return nil
}

// 此方法会返回一个ID，当对象中存在ID值的时候，返回的是对象中的ID，当对象中不存在ID值的时候，返回的
// 是一个新建的ID
func (this MgoCollection) hdCommon(obj interface{}, id string) string {
	tools.RftStruct(obj, func(ty reflect.Type, vl reflect.Value, tag reflect.StructTag) error {
		switch tag.Get("bson") {
		case "changed":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
				}
				vl.Elem().Set(reflect.ValueOf(time.Now().Unix()))
			}
		case "created":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
					vl.Elem().Set(reflect.ValueOf(time.Now().Unix()))
				}
			}
		case "_id":
			if ty.Kind() == reflect.Ptr {
				if vl.Pointer() == 0 {
					vl.Set(reflect.New(ty.Elem()))
					id = bson.NewObjectId().Hex()
					vl.Elem().Set(reflect.ValueOf(id))
				} else {
					if id == "" {
						id = vl.Elem().Interface().(string)
					} else {
						vl.Elem().Set(reflect.ValueOf(id))
					}
				}
			}

		}
		return nil
	})
	return id
}
